package cc.shacocloud.mirage.restful.util;

import cc.shacocloud.mirage.restful.HttpRequest;
import cc.shacocloud.mirage.restful.RoutingContext;
import cc.shacocloud.mirage.restful.bind.WebDataBinder;
import cc.shacocloud.mirage.restful.bind.validation.BindingResult;
import cc.shacocloud.mirage.utils.MethodParameter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class BindingResultUtil {
    
    /**
     * 是否在验证错误时引发致命绑定异常
     *
     * @param request   当前请求对象
     * @param binder    用于执行数据绑定的数据绑定器
     * @param parameter 方法参数描述符
     * @return 如果下一个方法参数的类型不是{@link Errors}，则为{@code true}
     */
    public static boolean isBindExceptionRequired(HttpRequest request, WebDataBinder binder, @NotNull MethodParameter parameter) {
        int i = parameter.getParameterIndex();
        Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
        boolean hasBindingResult = (paramTypes.length > (i + 1) && BindingResult.class.isAssignableFrom(paramTypes[i + 1]));
        // 如果下一个参数是 Errors 则添加到上下文中方便使用
        if (hasBindingResult) {
            bindBindingResult(request.context(), parameter, binder.getBindingResult());
        }
        
        return !hasBindingResult;
    }
    
    /**
     * 绑定{@link BindingResult}，用于判断参数验证错误时引发致命绑定异常，如果不引发则需要绑定{@link BindingResult}
     *
     * @param context       当前上下文
     * @param parameter     当前参数校验的参数属性
     * @param bindingResult 当前参数校验的{@link BindingResult}
     * @see #isBindExceptionRequired
     */
    public static void bindBindingResult(@NotNull RoutingContext context,
                                         @NotNull MethodParameter parameter,
                                         BindingResult bindingResult) {
        int i = parameter.getParameterIndex();
        context.put(RoutingContext.INTERNAL_PARAMETERS_PREFIX + BindingResult.MODEL_KEY_PREFIX + (i + 1), bindingResult);
    }
    
    /**
     * 获取已经绑定了的 {@link BindingResult}
     *
     * @param context   当前应用上下文
     * @param parameter 参数校验对象绑定的 {@link BindingResult} 对象的方法属性
     * @return {@link BindingResult}
     */
    @Nullable
    public static BindingResult getBindingResult(@NotNull RoutingContext context, @NotNull MethodParameter parameter) {
        int i = parameter.getParameterIndex();
        Object attr = context.get(RoutingContext.INTERNAL_PARAMETERS_PREFIX + BindingResult.MODEL_KEY_PREFIX + i);
        
        if (attr != null && !(attr instanceof BindingResult)) {
            throw new IllegalStateException("BindingResult 属性的类型不是 BindingResult：" + attr);
        }
        return (BindingResult) attr;
    }
    
    
}
